package com.df.my_net_game.game_rpc.client;

import com.df.my_net_game.common.config.MethodMetaInfo;
import com.df.my_net_game.game_rpc.EzGameMsg;
import com.df.my_net_game.game_rpc.client.annotation.GRpc;
import com.df.my_net_game.game_rpc.client.annotation.GRpcReq;
import com.df.my_net_game.game_rpc.config.GRpcServiceMgr;
import com.df.my_net_game.game_rpc.utils.TypeUtils;
import io.netty.util.concurrent.Promise;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class GRpcClientCenter {
    private static ConcurrentHashMap<Method, MethodMetaInfo> cache = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<Method,MethodMetaInfo> methodCache = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<Integer, Promise> reqs = new ConcurrentHashMap<>();
    //TODO 重新定义线程池，超过最大数量则直接返回失败
    private static ExecutorService executorService = Executors.newFixedThreadPool(5);


    @RuntimeType
    public static Object handleMethodCall(@Origin Method method, @AllArguments Object[] arg) {
        //只接收一个参数：
        // 基本数据类型：int bool long
        // 引用类型 list map set
        // 获取方法元信息
        MethodMetaInfo methodMetaInfo = cache.get(method);
        if(methodMetaInfo == null){
            throw new RuntimeException("未找到方法："+method.getName());
        }
        if(methodMetaInfo.getCmdId()==null){
            throw new RuntimeException("未标注@GrpcReq 或 未声明reqId");
        }

        //TODO 这里对返回的消息进行处理,ServerNode发送消息返回的都是Promise<RpcMsg>
        Promise<EzGameMsg> promise = GRpcServiceMgr.sendMsg(methodMetaInfo, arg);
        if(promise==null){
            return null;
        }
        //TODO
        return promise;
    }

    private static void cmdIdCheck(Method method, MethodMetaInfo methodMetaInfo) {
        GRpcReq annotation = method.getAnnotation(GRpcReq.class);
        if(annotation == null) {
            return;
        }
        int id = annotation.id();
        methodMetaInfo.setCmdId(id);
    }

    private static void checkParamType(Method method, MethodMetaInfo methodMetaInfo) {


        //TODO 支持多个，动态生成包装类?当前只支持1个参数
        Class<?>[] parameterTypes = method.getParameterTypes();

//        methodMetaInfo.setParamIsBaseType(true);
        if(parameterTypes!=null && parameterTypes.length>0){
            Class<?> parameterType = parameterTypes[0];
            if(parameterType.equals(Promise.class)){//异步类型，解析泛型

            }
            methodMetaInfo.setParameterType(parameterType);
            methodMetaInfo.setParamIsBaseType(TypeUtils.isBaseType(parameterType));
        }
    }



    private static void checkReturnType(Method method, MethodMetaInfo methodMetaInfo) {
        //TODO 如果是广播,则只支持void和list
        Class<?> returnType = method.getReturnType();
        Type genericReturnType = method.getGenericReturnType();
        if(returnType == void.class) {//无返回值
            methodMetaInfo.setReturnType(void.class);
            methodMetaInfo.setAsync(true);
            methodMetaInfo.setReturnIsBaseType(true);
            return;
        }
        //TODO 有返回值
        //基础类型？
        //Future类型？
        //引用类型（检测是不是被protobufclass注解了）
        methodMetaInfo.setReturnType(returnType);
        methodMetaInfo.setAsync(true);
        methodMetaInfo.setReturnIsBaseType(TypeUtils.isBaseType(returnType));
    }

    public static void addReqMethod(Class clazz,Method method, GRpc annotation) {
        MethodMetaInfo methodMetaInfo = cache.get(method);
        if(methodMetaInfo == null) {
            methodMetaInfo = new MethodMetaInfo();
            methodMetaInfo.setMethodName(method.getName());

            methodMetaInfo.setServer(annotation.server());

            String address = annotation.address();
            if(!address.isBlank()){
                String[] split = address.split(":");
                if(split.length<2){
                    throw new RuntimeException("address 配置错误，请检查配置！类："+clazz.getName()+" 方法："+method.getName()+" 配置address:"+address);
                }

                methodMetaInfo.setIp(split[0]);
                int port = 0;
                try {
                    port =  Integer.parseInt(split[1]);
                }catch (Exception e){
                    throw new RuntimeException("address 配置错误，请检查配置！类："+clazz.getName()+" 方法："+method.getName()+" 配置address:"+address);
                }
                methodMetaInfo.setPort(port);
            }

            checkReturnType(method,methodMetaInfo);
            checkParamType(method,methodMetaInfo);
            cmdIdCheck(method,methodMetaInfo);

            cache.put(method, methodMetaInfo);
        }
    }
}
